package com.gt.web.srv.aspect;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.gt.web.srv.utils.RequestUtil;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @Title: LogAspect
 * @Description: 日志切面
 */
@Aspect
@Component
@Log4j2
public class RequestLogAspect {

//    @Resource
//    private BizPeopleService bizPeopleService;

    /**
     * 防止jsonToString太长切割长度
     */
    public static final int CUT_LENGTH = 10000;

    /**
     * 获取当前Request
     *
     * @return
     */
    public static HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            return requestAttributes.getRequest();
        } else {
            RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
    }

    /**
     * 接口地址匹配
     *
     * @param uriList
     * @param uriStr
     * @return
     */
    public static boolean matches(List<String> uriList, String uriStr) {
        if (CollUtil.isEmpty(uriList)) {
            return false;
        }
        for (String item : uriList) {
            if (new AntPathMatcher().match(item, uriStr)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 转jsonString
     *
     * @param requestLog
     * @param cutLength
     * @return
     */
    public static String jsonToString(RequestLog requestLog, int cutLength) {
        String jsonStr = JSONUtil.toJsonStr(requestLog);
        return cutLength > 0 ? StrUtil.maxLength(jsonStr, cutLength) : jsonStr;
    }


    @Pointcut("@within(org.springframework.stereotype.Controller)")
    public void controllerMethods() {
        // This pointcut selects all methods within classes annotated with @Controller
    }

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void restControllerMethods() {
        // This pointcut selects all methods within classes annotated with @RestController
    }

    @Before(value = "(controllerMethods() || restControllerMethods())")
    public void methodBefore(JoinPoint joinPoint) {
        // 打印日志
        RequestLog requestLog = getLog(joinPoint, null, "request");
        log.info("RequestLog before:" + jsonToString(requestLog, ObjectUtil.defaultIfNull(CUT_LENGTH, 0)));
    }

    @AfterReturning(returning = "o", pointcut = "controllerMethods() || restControllerMethods()")
    public void methodAfterReturn(JoinPoint joinPoint, Object o) {
        // 打印日志
        RequestLog requestLog = getLog(joinPoint, o, "response");
        log.info("RequestLog after:" + jsonToString(requestLog, CUT_LENGTH));
    }

    @AfterThrowing(throwing = "e", pointcut = "controllerMethods() || restControllerMethods()")
    public void methodAfterThrow(JoinPoint joinPoint, Exception e) {
        // 打印日志
        RequestLog requestLog = getLog(joinPoint, null, "exception");
        log.info("RequestLog exception:" + jsonToString(requestLog, CUT_LENGTH));
    }

    private RequestLog getLog(JoinPoint joinPoint, Object o, String type) {
        RequestLog requestLog = null;
        HttpServletRequest request = getRequest();
        String traceId = RequestUtil.getTraceId(request);
        String requestURL = request.getRequestURL().toString();
        try {
            requestLog = new RequestLog();
            requestLog.setTraceId(traceId);
            requestLog.setRequestURL(requestURL);
            requestLog.setRestType(request.getMethod());
            requestLog.setMethodSignature(joinPoint.getSignature().toString());
            if (StrUtil.equalsIgnoreCase(type, "request")) {
                requestLog.setParams(joinPoint.getArgs());
            } else {
                requestLog.setReturnObject(o);
            }
        } catch (Exception e) {
            log.error("###LogAspect.class methodAfterReturn() ### ERROR:", e);
        }
        return requestLog;
    }


    @Data
    private static class RequestLog {
        // 请求地址
        private String requestURL;
        // 请求方式类型
        private String restType;
        // 请求方法名称
        private String methodSignature;
        // 请求方法参数
        private Object[] params;
        // 返回方法对象
        private Object returnObject;
        private Long userId;
        private String userName;
        private String requestIp;
        private String traceId;
    }
}
